import time
from typing import List, Dict, Optional

# main.py
import os
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from openai import OpenAI
from dotenv import load_dotenv

from pydantic import Field

# 加载环境变量
from starlette import requests

load_dotenv()

# 初始化 FastAPI 应用
app = FastAPI(
    title="SiliconFlow AI 代理",
    description="一个基于 FastAPI 和硅基流动的 AI 接口",
    version="1.0.0"
)

# 添加 CORS 中间件（如果前端与后端不同源）
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 在生产环境中应更严格
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 初始化硅基流动客户端
# 关键步骤：配置 base_url 为硅基流动的端点
client = OpenAI(
    api_key=os.getenv("SILICONFLOW_API_KEY"),
    base_url="https://api.siliconflow.cn/v1"  # 硅基流动的 API 端点
)


# 定义请求和响应模型
class ChatRequest(BaseModel):
    message: str
    model: str = os.getenv("DEFAULT_MODEL", "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B")  # 默认模型
    max_tokens: int = 8192
    temperature: float = 0.6


class ChatResponse(BaseModel):
    response: str
    model_used: str
    tokens_used: int = None


# 健康检查端点
@app.get("/")
async def root():
    return {"message": "硅基流动 AI 代理服务运行中"}


# 添加认证中间件:判断token有效、额度不足

# 主要的聊天端点
@app.post("/chat", response_model=ChatResponse)
async def chat_completion(request: ChatRequest):
    try:
        # 调用硅基流动 API
        response = client.chat.completions.create(
            model=request.model,
            messages=[
                {"role": "user", "content": request.message}
            ],
            max_tokens=request.max_tokens,
            temperature=request.temperature,
            stream=False  # 为了简单起见，这里不使用流式响应
        )

        # 提取回复内容
        ai_response = response.choices[0].message.content

        return ChatResponse(
            response=ai_response,
            model_used=request.model,
            tokens_used=response.usage.total_tokens if response.usage else None
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"AI 服务调用失败: {str(e)}")


class KeywordExpandRequest(BaseModel):
    keyword: str
    model: str = os.getenv("DEFAULT_MODEL", "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B")
    max_tokens: int = 500
    temperature: float = 0.3



class KeywordExpandResponse(BaseModel):
    base_keyword: str
    derived_keywords: List[str]
    model_used: str
    tokens_used: Optional[int] = None

#关键词生成以关键词生成关联的10衍生提示词
@app.post("/keywords/expand", response_model=KeywordExpandResponse)
async def expand_keywords(request: KeywordExpandRequest):
    """
    根据基础关键词生成10个关联的衍生关键词
    """
    try:
        # 构建关键词生成提示词
        prompt = f"""请基于基础关键词“{request.keyword}”，生成10个紧密关联的衍生关键词。

        生成要求：
        1. 从学习培训维度扩展：课程、培训、教学、学习路径
        2. 从教育推广维度扩展：知识普及、技能传授、应用指导
        3. 从商业行为维度扩展：产品推广、服务销售、商业应用
        4. 从市场需求维度扩展：就业机会、职业发展、行业需求
        5. 每个衍生词都要体现学习价值或商业价值

        特别强调：
        - 衍生词要包含具体的学习目标或商业应用场景
        - 体现教育培训、技能提升、商业变现等方向
        - 包含课程、培训、教程、认证、就业等关键词
        - 避免过于理论或技术化的词汇，侧重应用和需求
        
        输出格式要求：
        请严格按照以下JSON数组格式返回，不要任何额外文字：
        ["衍生词1", "衍生词2", "衍生词3", "衍生词4", "衍生词5", "衍生词6", "衍生词7", "衍生词8", "衍生词9", "衍生词10"]
        
        基础关键词：{request.keyword}"""

        # 调用硅基流动 API
        response = client.chat.completions.create(
            model=request.model,
            messages=[
                {"role": "user", "content": prompt}
            ],
            max_tokens=request.max_tokens,
            temperature=request.temperature,
            stream=False
        )

        # 提取回复内容
        ai_response = response.choices[0].message.content

        # 解析JSON响应
        import json
        try:
            derived_keywords = json.loads(ai_response)
            # 确保返回10个关键词，不足时补充
            if len(derived_keywords) < 10:
                derived_keywords.extend([""] * (10 - len(derived_keywords)))
        except json.JSONDecodeError:
            # 如果JSON解析失败，使用备用方案
            derived_keywords = _fallback_keyword_generation(request.keyword)

        return KeywordExpandResponse(
            base_keyword=request.keyword,
            derived_keywords=derived_keywords[:10],  # 确保只返回10个
            model_used=request.model,
            tokens_used=response.usage.total_tokens if response.usage else None
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"关键词生成失败: {str(e)}")


def _fallback_keyword_generation(base_keyword: str) -> List[str]:
    """备用关键词生成方案"""
    # 简单的关键词扩展逻辑
    templates = [
        f"{base_keyword}原理", f"{base_keyword}方法", f"{base_keyword}技术",
        f"{base_keyword}应用", f"{base_keyword}评估", f"{base_keyword}优化",
        f"{base_keyword}分析", f"{base_keyword}策略", f"{base_keyword}系统",
        f"{base_keyword}模型"
    ]
    return templates


class TemplateChatRequest(BaseModel):
    message: str
    template_name: str = "knowledge_qa"
    conversation_history: List[dict] = Field(default_factory=list)  # 使用默认工厂
    model: str = Field(default=os.getenv("DEFAULT_MODEL", "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B"))
    max_tokens: int = Field(default=8192, ge=1, le=32000)  # 添加验证
    temperature: float = Field(default=0.6, ge=0.0, le=2.0)


# 客服模版
PROMPT_TEMPLATES = {
    "scientific_explanation": """你是一位优秀的科学传播者，擅长将复杂概念通俗化。
            写作要求：
            1. 用生活化的类比解释科学原理
            2. 避免使用专业术语，如必须使用请加以解释
            3. 每个科学论断都要有现实例子支撑
            4. 保持客观中立，不夸大研究成果""",

    "technology_introduction": """你是科技专栏作家，介绍新兴技术。
                        写作指南：
                        - 从技术起源和发展历程说起
                        - 重点说明技术如何改变日常生活
                        - 分析技术面临的挑战和局限
                        - 展望未来5年的发展趋势
                        语言风格：生动有趣，贴近大众""",

    "health_science": """你是健康科普专家，基于证据进行科普。
                        基本原则：
                        1. 引用权威医学研究结果
                        2. 区分相关性和因果关系
                        3. 说明研究的局限性
                        4. 提供实用的健康建议
                        重要：不传播未经证实的健康信息""",
    "knowledge_qa": """你是一个知识问答专家，基于事实回答问题。
                    回答要求：
                    1. 基于可靠信息
                    2. 引用数据来源（如果知道）
                    3. 区分事实和观点
                    4. 不知道就说不知道"""
}


# 根据提示词返回内容
# 根据提示词返回内容
@app.post("/chat/with-prompt")
async def chat_with_template(request: TemplateChatRequest):
    # 获取系统提示模板
    system_prompt = PROMPT_TEMPLATES.get(
        request.template_name,
        PROMPT_TEMPLATES["knowledge_qa"]  # 默认回退
    )

    # 构建消息 - 修复这里
    messages = [{"role": "system", "content": system_prompt}]

    # 添加历史记录
    if request.conversation_history:
        messages.extend(request.conversation_history)

    # 添加当前消息
    messages.append({"role": "user", "content": request.message})

    # 调用API...
    try:
        # 调用硅基流动 API - 修复这里：直接传递 messages 而不是嵌套
        response = client.chat.completions.create(
            model=request.model,
            messages=messages,  # 直接使用构建好的 messages 列表
            max_tokens=request.max_tokens,
            temperature=request.temperature,
            stream=False
        )

        # 提取回复内容
        ai_response = response.choices[0].message.content

        return ChatResponse(
            response=ai_response,
            model_used=request.model,
            tokens_used=response.usage.total_tokens if response.usage else None
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"AI 服务调用失败: {str(e)}")


from fastapi.responses import StreamingResponse
import json
import re


# 流式响应版本
@app.post("/chat/stream")
async def chat_completion_stream(request: ChatRequest):
    """
    修正后的流式响应版本 - 使用同步迭代
    """

    def generate_stream():
        try:
            # 使用同步迭代而不是异步迭代
            stream = client.chat.completions.create(
                model=request.model,
                messages=[{"role": "user", "content": request.message}],
                max_tokens=request.max_tokens,
                temperature=request.temperature,
                stream=True
            )

            # 使用普通的 for 循环而不是 async for
            for chunk in stream:
                if chunk.choices and chunk.choices[0].delta.content is not None:
                    content = chunk.choices[0].delta.content
                    # 关键步骤：在服务器端解码 Unicode
                    # 返回格式化的数据
                    yield f"data: {json.dumps({'content': content, 'finish_reason': chunk.choices[0].finish_reason})}\n\n"

                # 添加微小延迟
                time.sleep(0.01)

        except Exception as e:
            error_msg = f"流式请求失败: {str(e)}"
            yield f"data: {json.dumps({'error': error_msg, 'content': ''})}\n\n"

    return StreamingResponse(
        generate_stream(),
        media_type="text/plain",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Access-Control-Allow-Origin": "*",
        }
    )


# 获取可用模型的端点（可选）
@app.get("/models")
async def get_available_models():
    """
    获取硅基流动上可用的模型列表。
    注意：硅基流动可能没有完全开放模型列表接口，
    你可能需要从他们的文档中手动维护一个列表。
    """
    # 这里只是一个示例，实际实现可能需要调整
    try:
        models = client.models.list()
        return {"models": [model.id for model in models.data]}
    except Exception as e:
        # 如果上面的方法不行，返回一个预定义的模型列表
        predefined_models = [
            "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B",
            "deepseek-ai/DeepSeek-V3",
            "deepseek-ai/DeepSeek-R1",
            "deepseek-ai/DeepSeek-Coder-V2",
            "Qwen/Qwen2.5-72B-Instruct",
            # 添加其他你在硅基流动上能访问的模型
        ]
        return {"models": predefined_models}


#AI智能体
#IntelligentAgent.py

#AI陪练
#ai_training.py

#AI客服



if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=39800)
